/*
 *  cgi.c
 *
 * A CGI/1.1 library for programs trying to read data requests from
 * a HTTP/1.1 web server using a GET or POST style request.  
 * Supported enctypes are: application/x-www-form-urlencoded and 
 * multipart/form-data.  WARNING: multipart/form-data doesn't support
 * recursivness.  File Uploads are supported, if the form-data was a 
 * file, the location of the temp downloaded file is placed in the the value.
 *
 * Copyright (c) 2004-2006 Karlan Thomas Mitchell<karlanmitchell@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *         This product includes software developed by
           Karlan Thomas Mitchell<karlanmitchell@gmail.com>
 * 4. Neither Karlan Thomas Mitchell nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY KARLAN THOMAS MITCHELL AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "cgi.h"

static size_t max_form_bytes = 32768;


int cgi_get_getdata(char * buffer, size_t buffer_len){
    if(buffer_len < 1)/*WTF?*/
        return -1;
    buffer[buffer_len - 1] = 0;
    char * query_pnt    = getenv("QUERY_STRING");
    if(query_pnt == NULL)
        return -1;
    strncpy(buffer, query_pnt, buffer_len);
    return 0;
}


int header_get_item(char * header, char *name, char *value, size_t value_len){
	
	*value = 0;
	char *startpntr;
	char *endpntr;
	char backup;
	char name_buf[100];
	snprintf(name_buf, 100, "%s=\"", name);


	startpntr = strstr(header, name_buf);
	if(startpntr == NULL)
		return -1;
	startpntr = strchr(startpntr, '\"');
	if(startpntr == NULL)
		return -1;
	*(startpntr++);
	endpntr = strchr(startpntr, '\"');
	if(endpntr == NULL)
		return -1;
	backup = *endpntr;
	*endpntr = 0;
	strlcpy(value, startpntr, value_len);
	*endpntr = backup;
	

	return 0;
	}



int cgi_get_postdata(struct cgienviron * cgistruct, char * buffer, size_t buffer_len){
    size_t read_size = 100;
    size_t just_read = 0;
    size_t total_read = 0;
    char * content_type_pnt;
    char * method_pnt;
    char * content_length_pnt;
    content_type_pnt   = getenv("CONTENT_TYPE");
    method_pnt         = getenv("REQUEST_METHOD");
    content_length_pnt = getenv("CONTENT_LENGTH");

//    printf("content-type: %s\n", content_type_pnt);

    /*Error Checking...*/
    if(content_type_pnt == NULL ||
       (strncasecmp(content_type_pnt, "application/x-www-form-urlencoded", strlen("application/x-www-form-urlencoded")) != 0 && /*simple cgi args*/
       strncasecmp(content_type_pnt, "multipart/form-data", strlen("multipart/form-data")) != 0))/*Usually a file*/
       return -1;

    if(method_pnt == NULL ||
       strcasecmp(method_pnt, "POST") != 0)
       return -1;

    if(content_length_pnt == NULL)
        return -1;


    size_t content_length = atol(content_length_pnt);


    char * post_buf    = malloc(read_size * sizeof(char));
    if(post_buf == NULL)
        return -1;



    if(strncasecmp(content_type_pnt, "multipart/form-data", strlen("multipart/form-data")) == 0){
    	char content_divider[1024];
    	char content_disposition[1024];
    	char line_buffer[1024];
    	char line_buffer2[1024];
    	char name[1024];
    	char filename[1024];
    	char tmp_filename[1024];

	ssize_t rv;
	ssize_t rv2;
    	if( (rv = freadline_r(stdin, content_divider, 1024)) <= 0)
		return -1;

//    	printf("Content Boundary: %s\n", content_divider);fflush(stdout);
	
	while(rv != 0){


		*content_disposition = 0;
		while(rv > 0){
			rv = freadline_r(stdin, line_buffer, 1024);	
//   			printf("2buffer: %s\n", line_buffer);fflush(stdout);
			if(strncasecmp(line_buffer, "Content-Disposition", strlen("Content-Disposition")) == 0){
				str_splitup(line_buffer, line_buffer, 0, content_disposition, 1024, ":");
//   				printf("Content Disposition: %s\n", content_disposition);fflush(stdout);
			}
		}
		if(strlen(content_disposition) == 0)
			break;

		header_get_item(content_disposition, "name", name, 1024);
		
		if(header_get_item(content_disposition, "filename", filename, 1024) == 0){
			

			create_tmp_file(filename, tmp_filename, 1024);
//  			printf("temp_file: %s\n", tmp_filename);
			fflush(stdout);

			if(strlen(filename) != 0){
				mdb_set(cgistruct, name, tmp_filename);
				snprintf(buffer, 1024, "%s.filename", name);
				mdb_set(cgistruct, buffer, filename);
			}
			FILE * tmp_fileout = fopen(tmp_filename, "wb");


 			while(1){

				rv = core_readline_r_timeout((void *)stdin, line_buffer, 1024, MODE_ND_FILE, NULL);
//   				printf("buffer: %s\n", line_buffer);fflush(stdout);
 				if(strncmp(line_buffer, content_divider, strlen(content_divider)) == 0)
					break;
					
				fwrite(line_buffer, 1, rv, tmp_fileout);
        			
 			}
			fclose(tmp_fileout);

			truncate(tmp_filename, getfilesize(tmp_filename) - 2);

			

			if(strlen(filename) == 0)
				remove(tmp_filename);
		}
		else{
 			while(1){

				rv = core_readline_r_timeout((void *)stdin, line_buffer, 1024, MODE_ND_FILE, NULL);
// 				printf("buffer: %s\n", line_buffer);
 				if(strncmp(line_buffer, content_divider, strlen(content_divider)) == 0)
					break;

				
				mdb_cat(cgistruct, name, line_buffer);
        			
 			}
			int entry_len = strlen(mdb_print(cgistruct, name));
			if(entry_len >= 2)
				mdb_print(cgistruct, name)[entry_len - 2] = 0;
			
		}

	}
	


    }/*END: if multipart/form-data*/
    else{
    	buffer[0] = 0;
    	for(total_read = 0 ; total_read < content_length ; ){
        	just_read = fread(post_buf, 1, 1, stdin);
		if(just_read == 0)
			break;
		post_buf[just_read] = 0;
        	/*Check if we are flushing the data(ie buffer full), or copying*/
        	if(total_read < buffer_len - 1)
            		strlcat(buffer, post_buf, buffer_len);
        
        	total_read += just_read;
    	}
    }

    free(post_buf);
    return 0;
}

/*
int cgi_init_r(struct cgienviron * cgistruct, struct httpconnection * httpstruct){
    return cgi_init_core(cgistruct, 1, httpstruct);
}*/

/*The core should be CGI/1.1 compliant */
int cgi_init(struct cgienviron * cgistruct){
    mdb_init(cgistruct);
    /*Just in case this fail, a free won't do any harm*/
//     cgistruct->items = 0;
//     cgistruct->name  = NULL;
//     cgistruct->value = NULL;

    char * method;
    char * content_type;

    method       = getenv("REQUEST_METHOD");
    content_type = getenv("CONTENT_TYPE");

    if(method == NULL)
	return -1;

    char * query_buffer =  malloc(max_form_bytes * sizeof(char));
    if(query_buffer == NULL)
        return -1;

  if(strcmp(method, "POST") == 0){
  	if(cgi_get_postdata(cgistruct, query_buffer, max_form_bytes))
             query_buffer[0] = 0;
  }
  else if(cgi_get_getdata(query_buffer, max_form_bytes))
             query_buffer[0] = 0;

    char * strtok_buf;
    char * entry;




    if(content_type == NULL || strncasecmp(content_type, "application/x-www-form-urlencoded", strlen("application/x-www-form-urlencoded")) == 0){
    	entry = (char *)strtok_r(query_buffer, "&", &strtok_buf);


    	while(entry != NULL){
        	cgi_add_entry(cgistruct, entry);
        	entry = (char *)strtok_r(NULL, "&", &strtok_buf);
    	}
    }

    free(query_buffer);
    return 0;
}



int cgi_add_entry(struct cgienviron * cgistruct, char * encoded_entry){
    char * strtok_buf;
    char * name;
    char * value;
    char   zero = '\0';


    name      = (char *)strtok_r(encoded_entry, "=", &strtok_buf);
    value     = (char *)strtok_r(NULL, "=", &strtok_buf);;

    if(name == NULL)
        return -1;
    if(value == NULL)
	value = &zero;

    mdb_add_escaped_value(cgistruct, name, value);


    return 0;
}





